// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MarketField.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Using Directives

    using System;

    using Org.OpenTrader.Framework.Forge.Enums;

    #endregion

    /// <summary>
    /// The market field cell.
    /// </summary>
    [Serializable]
    public class MarketField
    {
        #region Constants and Fields

        /// <summary>
        /// The stamp.
        /// </summary>
        private DateTime stamp;

        /// <summary>
        /// The value.
        /// </summary>
        private object value;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarketField"/> class.
        /// </summary>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        public MarketField(EMarketField fieldName)
        {
            this.FieldName = fieldName;
            this.Value = null;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MarketField"/> class.
        /// </summary>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        /// <param name="defaultValue">
        /// The default value.
        /// </param>
        public MarketField(EMarketField fieldName, object defaultValue)
        {
            this.FieldName = fieldName;
            this.Value = defaultValue;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets FieldName.
        /// </summary>
        public EMarketField FieldName { get; private set; }

        /// <summary>
        /// The has value.
        /// </summary>
        /// <returns>
        /// The has value.
        /// </returns>
        public bool HasValue
        {
            get
            {
                return this.value != null;
            }
        }

        /// <summary>
        /// Gets a value indicating whether IsDateTime.
        /// </summary>
        public bool IsDateTime
        {
            get
            {
                return this.HasValue && this.value is DateTime;
            }
        }

        /// <summary>
        /// Gets a value indicating whether IsDouble.
        /// </summary>
        public bool IsDouble
        {
            get
            {
                return this.HasValue && this.value is double;
            }
        }

        /// <summary>
        /// Gets a value indicating whether IsInt.
        /// </summary>
        public bool IsInt
        {
            get
            {
                return this.HasValue && this.value is int;
            }
        }

        /// <summary>
        /// Gets a value indicating whether IsString.
        /// </summary>
        public bool IsString
        {
            get
            {
                return this.HasValue && this.value is string;
            }
        }

        /// <summary>
        /// Gets TimeStamp.
        /// </summary>
        public DateTime TimeStamp
        {
            get
            {
                return this.stamp;
            }
        }

        /// <summary>
        /// Gets Value.
        /// </summary>
        public object Value
        {
            get
            {
                return this.value;
            }

            set
            {
                if (value == null)
                {
                    this.value = null;
                    this.stamp = DateTime.MinValue;
                    return;
                }

                if (((!(value is string) && !(value is double)) && !(value is int)) && !(value is DateTime))
                {
                    throw new Exception("Value should only be string, double, int or datetime");
                }

                this.stamp = Timings.Now;
                this.value = value;
            }
        }

        #endregion

        #region Operators

        /// <summary>
        /// The op_ implicit.
        /// </summary>
        /// <param name="cell">
        /// The cell.
        /// </param>
        /// <returns>
        /// </returns>
        /// <exception cref="Exception">
        /// </exception>
        public static implicit operator string(MarketField cell)
        {
            if (cell.value == null)
            {
                throw new Exception("Cells has no value !");
            }

            if (cell.value is string)
            {
                return cell.value as string;
            }

            return cell.value.ToString();
        }

        /// <summary>
        /// The op_ implicit.
        /// </summary>
        /// <param name="cell">
        /// The cell.
        /// </param>
        /// <returns>
        /// </returns>
        /// <exception cref="Exception">
        /// </exception>
        /// <exception cref="InvalidCastException">
        /// </exception>
        public static implicit operator double(MarketField cell)
        {
            if (cell.value == null)
            {
                throw new Exception("Cells has no value !");
            }

            if (cell.value is double)
            {
                return (double)cell.value;
            }

            if (cell.value is string)
            {
                try
                {
                    return double.Parse(cell.value as string, Constants.CultureInfo.NumberFormat);
                }
                catch
                {
                    goto error;
                }
            }

            if (cell.value is int)
            {
                return (double)cell.value;
            }

            error:
            throw new InvalidCastException("Cells value is not convertible to double");
        }

        /// <summary>
        /// The op_ implicit.
        /// </summary>
        /// <param name="cell">
        /// The cell.
        /// </param>
        /// <returns>
        /// </returns>
        /// <exception cref="Exception">
        /// </exception>
        /// <exception cref="InvalidCastException">
        /// </exception>
        public static implicit operator DateTime(MarketField cell)
        {
            if (cell.value == null)
            {
                throw new Exception("Cells has no value !");
            }

            if (cell.value is DateTime)
            {
                return (DateTime)cell.value;
            }

            if (cell.value is string)
            {
                try
                {
                    return DateTime.Parse(cell.value as string, Constants.CultureInfo.NumberFormat);
                }
                catch
                {
                    goto error;
                }
            }

            error:
            throw new InvalidCastException("Cells value is not convertible to DateTime");
        }

        /// <summary>
        /// The op_ implicit.
        /// </summary>
        /// <param name="cell">
        /// The cell.
        /// </param>
        /// <returns>
        /// </returns>
        /// <exception cref="Exception">
        /// </exception>
        /// <exception cref="InvalidCastException">
        /// </exception>
        public static implicit operator int(MarketField cell)
        {
            if (cell.value == null)
            {
                throw new Exception("Cells has no value !");
            }

            if (cell.value is int)
            {
                return (int)cell.value;
            }

            if (cell.value is string)
            {
                try
                {
                    return int.Parse(cell.value as string, Constants.CultureInfo.NumberFormat);
                }
                catch
                {
                    goto error;
                }
            }

            if (cell.value is double)
            {
                return (int)((double)cell.value);
            }

            error:
            throw new InvalidCastException("Cells value is not convertible to int");
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The unset value.
        /// </summary>
        public void UnsetValue()
        {
            this.stamp = Timings.Now;
            this.value = null;
        }

        #endregion
    }
}